
/******************************************************************
Copyright © PsuCtl 2017~2023. All rights reserved.
* @projectName   PsuCtl
* @author        Raiiwhen
* @email         masterraii@icloud.com
* @origin        ESP i2c_oled_expamle
    * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
    * SPDX-License-Identifier: CC0-1.0
*******************************************************************/

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "esp_lvgl_port.h"
#include "lvgl.h"
#include "esp_lcd_panel_vendor.h"

extern i2c_master_bus_handle_t tool_bus_handle;

static const char *TAG = "gui";
static lv_obj_t *label;
static lv_obj_t *currChan[8];
static lv_obj_t *live;
static SemaphoreHandle_t g_uiSem;

#define LCD_H_RES              128
#define LCD_V_RES              64

void GuiLayoutInit(lv_disp_t *disp);

void panel_init(void)
{
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_i2c_config_t io_config = {
        .dev_addr = 0x3c,
        .scl_speed_hz = (400 * 1000),
        .control_phase_bytes = 1,               // According to SSD1306 datasheet
        .lcd_cmd_bits = 8,   // According to SSD1306 datasheet
        .lcd_param_bits = 8, // According to SSD1306 datasheet
        .dc_bit_offset = 6,                     // According to SSD1306 datasheet
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(tool_bus_handle, &io_config, &io_handle));

    ESP_LOGI(TAG, "Install SSD1306 panel driver");
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .bits_per_pixel = 1,
        .reset_gpio_num = -1,
    };
    esp_lcd_panel_ssd1306_config_t ssd1306_config = {
        .height = LCD_V_RES,
    };
    panel_config.vendor_config = &ssd1306_config;
    ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

    ESP_LOGI(TAG, "Initialize LVGL");
    const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    lvgl_port_init(&lvgl_cfg);

    const lvgl_port_display_cfg_t disp_cfg = {
        .io_handle = io_handle,
        .panel_handle = panel_handle,
        .buffer_size = LCD_H_RES * LCD_V_RES,
        .double_buffer = true,
        .hres = LCD_H_RES,
        .vres = LCD_V_RES,
        .monochrome = true,
        .rotation = {
            .swap_xy = false,
            .mirror_x = false,
            .mirror_y = false,
        }
    };
    lv_disp_t *disp = lvgl_port_add_disp(&disp_cfg);
    lv_disp_set_rotation(disp, LV_DISP_ROT_180);

    ESP_LOGI(TAG, "Display LVGL Scroll Text");
    if (lvgl_port_lock(0)) {
        GuiLayoutInit(disp);
        lvgl_port_unlock();
    }
    g_uiSem = xSemaphoreCreateMutex();
}

void GuiLayoutInit(lv_disp_t *disp)
{
    lv_obj_t *scr = lv_disp_get_scr_act(disp);
    label = lv_label_create(scr);
    lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); /* Circular scroll */
    lv_label_set_text(label, "PsuCtl Ver:\n" __DATE__ "\r\n" __TIME__);
    /* Size of the screen (if you use rotation 90 or 270, please set disp->driver->ver_res) */
    lv_obj_set_width(label, disp->driver->hor_res);
    lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0);
    for (int ch = 0; ch < 8; ch++) {
        currChan[ch] = lv_label_create(scr);
        lv_label_set_text(currChan[ch], "0.0");
        lv_obj_set_width(currChan[ch], disp->driver->hor_res / 4);
        lv_obj_align(currChan[ch], LV_ALIGN_TOP_LEFT, ch % 4 * 32, 32 + ch / 4 * 16);
    }
    live = lv_label_create(scr);
    lv_label_set_text(live, LV_SYMBOL_POWER);
    lv_obj_align(live, LV_ALIGN_TOP_RIGHT, 0, 0);
}

void GuiUpdateInfo(const float* currCh)
{
    static char str[128] = {0};
    static int heartBeat = 1;
    for (int ch = 0; ch < 8; ch++) {
        sprintf(str, "%.2f", currCh[ch]);
        lv_label_set_text(currChan[ch], currCh[ch] == 0.0f ? "_" : str);
    }
    lv_label_set_text(live, (heartBeat ^= 1) ? LV_SYMBOL_CLOSE : LV_SYMBOL_PLUS);
}

void GuiUpdateNetworkState(const char* state)
{
    xSemaphoreTake(g_uiSem, 0);
    lv_label_set_text(label, state);
    xSemaphoreGive(g_uiSem);
}
